=begin

This file is part of the arena-ruby-sandbox project, http://arena-ruby-sandbox.googlecode.com

Copyright (c) 2009-2009 Roberto Dario Seminara <robertodarioseminara@gmail.com>

arena-ruby-sandbox is free software: you can redistribute it and/or modify
it under the terms of the gnu general public license as published by
the free software foundation, either version 3 of the license, or
(at your option) any later version.


arena-ruby-sandbox is distributed in the hope that it will be useful,
but without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.  see the
gnu general public license for more details.

you should have received a copy of the gnu general public license
along with arena-ruby-sandbox.  if not, see <http://www.gnu.org/licenses/>.
=end


module RubySandbox
class ContextSwitcher

	def initialize( sandbox, inner_object, permitted_methods )
		@sandbox = sandbox
		@inner_object = inner_object
		@permitted_methods = permitted_methods

			permitted_methods = ["Array", "Float", "Integer", "String", "__method__",
				"sleep", "local_variables", "proc", "lambda", "loop", "throw", "raise", "rand",
				 "format", "print", "printf", "srand", "singleton_method_added", "eval", "block_given?"]
			# anular todos los metodos privados excepto los estrictamente necesarios del objeto

			(self.private_methods - permitted_methods).each do |m|
				codigo_anulacion = "def self.#{m} (*x)\n
						raise NoMethodError.new\n
						end\n"
						
				begin
				eval(codigo_anulacion)
				rescue RuntimeError => e
					unless e.to_s =~ /Illegal use of singleton_method_added/
						raise e
					end
				end
			end

		def singleton_method_added(m)
			alternate_singleton_method_added(m)
			#raise "xxxxxxxxxxxx"
		end

		# el ContextSwitcher se freeza a si mismo para que no puedan definirle metodos singleton
		self.freeze
	end

	def alternate_singleton_method_added(m)

		def singleton_method_added(m)
		end

		codigo_anulacion_ = "def self.#{m} (*x)\n
		raise NoMethodError.new\n
		end\n"
		
		eval(codigo_anulacion_)

		def singleton_method_added(m)
			alternate_singleton_method_added(m)
		end
		raise "Illegal use of singleton_method_added"
	end
		
	def instance_variable_get( varname )
		raise "Illegal use of instance_variable_get"
	end

	def instance_variable_set( varname, obj )
		raise "Illegal use of instance_variable_set"
	end

	def instance_eval
		raise "Illegal use of instance_eval"
	end
	
	def to_s
		self.method_missing("to_s")
	end
	
	def self.inspect
		self.method_missing("inspect")
	end

	def method_missing(m, *args)

		sandbox = @sandbox
		inner_object = @inner_object
		
		if @permitted_methods.select{|x| x.to_s == m.to_s}.size > 0 then # preguntarle al sandbox si permite este metodo
		
			retval = nil
			wrapped_retval = nil
			sandbox.privileged_block do # crear un bloque con privilegios
						# para que el metodo invocado pueda hacer
						# cosas normales sin ninguna restriccion

				if block_given?
					
					args_ = args.map{ |a| sandbox.wrap_out(a) }
					retval = inner_object.send(m,*args_) do |*x|
						sandbox.unprivileged_block do
							# sacar los privilegios porque el codigo
							# invocado en el yield esta adentro del sandbox
							
							x_ = x.map{ |a| sandbox.wrap_in(a) }
							yield(*x_)
						end
					end

				else
					args_ = args.map{ |a| sandbox.wrap_out(a) }
					retval = inner_object.send(m,*args_)
				end
				
				wrapped_retval = sandbox.wrap_in(retval)
				
			end
			
			wrapped_retval
		else
			raise "prohibited method #{m}"

		end
	end

end
end
